package org.ahmadsoft.ropes.impl;

import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;

import org.ahmadsoft.ropes.Rope;

/**
 * Special Rope created from java.lang.String, contains String specific
 * performance improvements.
 * 
 * @author Elli Albek
 */

public final class StringRope extends FlatCharSequenceRope {

	final String sequence;
	final int length;

	public StringRope(String s) {
		super(s);
		this.sequence = s;
		this.length = s.length();
	}

	@Override
	public int indexOf(char ch) {
		return sequence.indexOf(ch, 0);
	}

	@Override
	public int indexOf(char ch, int fromIndex) {
		return sequence.indexOf(ch, fromIndex);
	}

	@Override
	public int indexOf(CharSequence seq, int fromIndex) {
		String opt = stringParamOptimization(seq);
		if (opt != null)
			return sequence.indexOf(opt, fromIndex);

		return super.indexOf(seq, fromIndex);
	}

	public int lastIndexOf(int ch) {
		return sequence.lastIndexOf(ch);
	}

	public int lastIndexOf(int ch, int fromIndex) {
		return sequence.lastIndexOf(ch, fromIndex);
	}

	public int lastIndexOf(String str) {
		return sequence.lastIndexOf(str);
	}

	@Override
	public boolean endsWith(CharSequence suffix) {
		String opt = stringParamOptimization(suffix);
		if (opt != null)
			return sequence.endsWith(opt);

		return super.endsWith(suffix);
	}

	@Override
	public boolean startsWith(CharSequence prefix, int offset) {
		String opt = stringParamOptimization(prefix);
		if (opt != null)
			sequence.startsWith(opt, offset);

		return super.startsWith(prefix, offset);
	}

	@Override
	public Iterator<Character> iterator(final int start) {
		if (start < 0 || start > this.length())
			throw new IndexOutOfBoundsException("Rope index out of range: " + start);
		return new Iterator<Character>() {
			int current = start;
			final String sequence = StringRope.this.sequence;
			final int length = StringRope.this.length();
			
			public boolean hasNext() {
				return this.current < length;
			}

			public Character next() {
				return sequence.charAt(this.current++);
			}

			public void remove() {
				throw new UnsupportedOperationException("Rope iterator is read-only.");
			}
		};
	}
	
	/**
	 * Reduce depth of string chain
	 */
	@Override
	public Rope subSequence(int start, int end) {
		return new StringRope(sequence.substring(start, end));
	}

	// override for inlining
	@Override
	public char charAt(int index) {
		return sequence.charAt(index);
	}
	
	// override for inlining
	@Override
	public int length() {
		return length;
	}
	
	/**
	 * Doing it this way reduces depth
	 */
	@Override
	public Rope trim() {
		return new StringRope(sequence).trim();
	}

	@Override
	public void write(Writer out) throws IOException {
		out.write(sequence);
	}

	@Override
	public void write(Writer out, int offset, int length) throws IOException {
		// no need to check for illegal offset/length, the writer should do this
		out.write(sequence, offset, length);
	}

	private static final String stringParamOptimization(CharSequence seq) {
		if (seq instanceof String)
			return (String) seq;

		if (seq.length() <= 32)
			return seq.toString();

		// can't optimize
		return null;
	}
}
